<!DOCTYPE html>
<html><meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>2022 03 26</title>
<style type="text/css">
    body{
        background-color: #3C3F41;
    }
    a{
        text-decoration: none;
        color: #fbfbff;
    }
</style>
<body><head>
    <style type="text/css">
        nav ul {
            list-style-type: none;
            margin: 0;
            padding: 0;
            overflow: hidden;
            background-color: #333;
            position: -webkit-sticky;  
            position: sticky;
            top: 0;
        }

        nav li {
            float: left;
        }

        nav li a {
            display: block;
            color: white;
            text-align: center;
            padding: 14px 16px;
            text-decoration: none;
        }

         
        nav li a:hover {
            background-color: #111;
        }
        .active {
            background-color: #4CAF50;
        }
    </style>
</head>

<nav>
    <ul>
        
        
        <li><a href="/blog/">home</a></li>
         
        
        <li><a href="/blog/post">博文</a></li>
         
        
        <li><a href="/blog/about">about</a></li>
         
    </ul>
</nav>
<div id="content" style="margin: auto;width: 50%;padding: 10px; color: #ffffff;height: 100%">
    <article>
        <header>
            <h1 class="post-title">2022 03 26</h1>
        </header>
        <h3 id="mysql-乐观锁悲观锁">mysql 乐观锁,悲观锁</h3>
<p><strong>MySQL InnoDB中使用悲观锁</strong></p>
<p>要使用悲观锁，必须关闭mysql数据库的自动提交属性，因为MySQL默认使用autocommit模式，也就是当你执行一个更新操作后，MySQL会立即将结果进行提交</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-mysql" data-lang="mysql"><span style="display:flex;"><span><span style="color:#f92672">//</span><span style="color:#960050;background-color:#1e0010">开始事务</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>begin;<span style="color:#f92672">/</span>begin work;<span style="color:#f92672">/</span>start transaction;<span style="color:#960050;background-color:#1e0010">（三者选一个）</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">select</span> status <span style="color:#66d9ef">from</span> t_goods <span style="color:#66d9ef">where</span> id<span style="color:#f92672">=</span><span style="color:#ae81ff">1</span> <span style="color:#66d9ef">for</span> <span style="color:#66d9ef">update</span>;
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#f92672">//</span><span style="color:#960050;background-color:#1e0010">根据商品信息生成订单</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">insert</span> <span style="color:#66d9ef">into</span> <span style="color:#a6e22e">t_orders</span> (id,goods_id) <span style="color:#66d9ef">values</span> (<span style="color:#66d9ef">null</span>,<span style="color:#ae81ff">1</span>);
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#f92672">//</span><span style="color:#960050;background-color:#1e0010">修改商品</span>status为2
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">update</span> t_goods <span style="color:#66d9ef">set</span> status<span style="color:#f92672">=</span><span style="color:#ae81ff">2</span>;
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#f92672">//</span> <span style="color:#960050;background-color:#1e0010">提交事务</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>commit;<span style="color:#f92672">/</span>commit work;
</span></span></code></pre></div><p>以上查询语句中，使用了select&hellip;for update方式，通过开启排他锁的方式实现了悲观锁。则相应的记录被锁定，其他事务必须等本次事务提交之后才能够执行</p>
<p>我们使用select &hellip; for update会把数据给锁定，不过我们需要注意一些锁的级别，MySQL InnoDB默认行级锁。行级锁都是基于索引的，如果一条SQL用不到索引是不会使用行级锁的，会使用表级锁把整张表锁住。</p>
<p><strong>特点</strong></p>
<p>为数据处理的安全提供了保证</p>
<p>效率上，由于处理加锁的机制会让数据库产生额外开销，增加产生死锁机会</p>
<p>在只读型事务中由于不会产生冲突，也没必要使用锁，这样会增加系统负载，降低并行性</p>
<p>**乐观锁
**</p>
<p>乐观并发控制也是一种并发控制的方法。</p>
<p>假设多用户并发的事务在处理时不会彼此互相影响，各事务能够在不产生锁的情况下处理各自影响的那部分数据，在提交数据更新之前，每个事务会先检查在该事务读取数据后，有没其他事务修改该数据，如果有则回滚正在提交的事务</p>
<p>乐观锁相对悲观锁而言，是假设数据不会发生冲突，所以在数据进行提交更新的时候，才会正式对数据的冲突与否进行检测，如果发现冲突了，则让返回用户错误信息，让用户决定如何做</p>
<p>乐观锁实现一般使用记录版本号，为数据增加一个版本标识，当更新数据的时候对版本标识进行更新</p>
<p><strong>实现</strong></p>
<p>使用版本号时，可以在数据初始化时指定一个版本号，每次对数据的更新操作都对版本号执行+1操作。并判断当前版本号是不是该数据的最新版本号</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-mysql" data-lang="mysql"><span style="display:flex;"><span><span style="color:#ae81ff">1</span>.<span style="color:#960050;background-color:#1e0010">查询出商品信息</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">select</span> (status,status,version) <span style="color:#66d9ef">from</span> t_goods <span style="color:#66d9ef">where</span> id<span style="color:#f92672">=</span><span style="color:#75715e">#{id}
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#ae81ff">2</span>.<span style="color:#960050;background-color:#1e0010">根据商品信息生成订单</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ae81ff">3</span>.<span style="color:#960050;background-color:#1e0010">修改商品</span>status为2
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">update</span> t_goods
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">set</span> status<span style="color:#f92672">=</span><span style="color:#ae81ff">2</span>,version<span style="color:#f92672">=</span>version<span style="color:#f92672">+</span><span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">where</span> id<span style="color:#f92672">=</span><span style="color:#75715e">#{id} and version=#{version};
</span></span></span></code></pre></div><ul>
<li>
<p>所谓事务管理，其实就是“按照给定的事务规则来执行提交或者回滚操作”。</p>
<p><a href="https://www.colabug.com/tag/spring/">Spring</a> 并不直接管理事务，而是提供了多种事务管理器，他们将事务管理的职责委托给 Hibernate 或者 JTA 等持久化机制所提供的相关平台框架的事务来实现。</p>
<p>Spring 事务管理器接口： <code>org.springframework.transaction.PlatformTransactionManager</code>
，通过这个接口，Spring 为各个平台如 JDBC、Hibernate 等都提供了对应的事务管理器，但是具体的实现就是各个平台自己的事情了。</p>
<h3 id="spring-事务的分类">Spring 事务的分类</h3>
<p>Spring 提供了两种事务管理方式： <strong>声明式事务管理</strong>
和 <strong>编程式事务管理</strong>
。对不同的持久层访问技术，编程式事务提供一致的事务编程风格，通过模板化的操作一致性地管理事务；而声明式事务基于 Spring AOP 实现，却并不需要程序开发者成为 AOP 专家，亦可轻易使用 Spring 的声明式事务管理。</p>
<ul>
<li>声明式事务</li>
</ul>
<p>Spring 的声明式事务管理是建立在 Spring AOP 机制之上的，其本质是对目标方法前后进行拦截，并在目标方法开始之前创建或者加入一个事务，在执行完目标方法之后根据执行情况提交或者回滚事务。</p>
<p>简单地说，声明式事务是 <strong>编程式事务 + AOP 技术</strong>
包装，使用注解进行扫包，指定范围进行事务管理。声明式事务管理要优于编程式事务管理，这正是 Spring 倡导的非侵入式的开发方式</p>
<ul>
<li>编程式事务</li>
</ul>
<p>在 Spring 出现以前，编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中显式调用 beginTransaction()、commit()、rollback() 等事务管理相关的方法，这就是编程式事务管理。</p>
<p>简单地说，编程式事务就是在代码中显式调用开启事务、提交事务、回滚事务的相关方法。</p>
<h3 id="spring-事务的原理">Spring 事务的原理</h3>
<p>使用 AOP <strong>环绕通知</strong>
和 <strong>异常通知</strong>
。</p>
<p>注意： 在使用 Spring 事务时不能使用 <code>try-catch</code>
进行异常捕获，要将异常抛给外层，使其进行异常拦截，触发事务机制。</p>
<h3 id="事务的传播行为">事务的传播行为</h3>
<p>所谓事务的传播行为是指,如果在开始当前事务之前，一个事务上下文已经存在，此时有若干选项可以指定一个事务性方法的执行行为。</p>
<p>REQUIRED：如果当前存在事务，则加入该事务；如果当前没有事务，则创建一个新的事务。这是默认值。</p>
<p>REQUIRES_NEW：创建一个新的事务，如果当前存在事务，则把当前事务挂起。</p>
<p>SUPPORTS：如果当前存在事务，则加入该事务；如果当前没有事务，则以非事务的方式继续运行。</p>
<p>NOT_SUPPORTED：以非事务方式运行，如果当前存在事务，则把当前事务挂起。</p>
<p>NEVER：以非事务方式运行，如果当前存在事务，则抛出异常。</p>
<p>MANDATORY：如果当前存在事务，则加入该事务；如果当前没有事务，则抛出异常。</p>
<p>NESTED：如果当前存在事务，则创建一个事务作为当前事务的嵌套事务来运行；如果当前没有事务，则该取值等价于 <strong>REQUIRED</strong>
。</p>
</li>
</ul>
<h3 id="基于注解-transactional-声明事务失效分析">基于注解 @Transactional 声明事务失效分析</h3>
<p>在开发过程中，可能会遇到使用 @Transactional 进行事务管理时出现失效的情况。</p>
<h4 id="常见失效场景">常见失效场景</h4>
<ul>
<li>
<p>如果使用 MySQL 且引擎是 MyISAM，则事务会不起作用，原因是 MyISAM 不支持事务，改成 InnoDB 引擎则支持事务。</p>
</li>
<li>
<p>注解 @Trasactional 只能加在 <code>public</code>
修饰的方法上事务才起效。如果加在 <code>protect</code>
、 <code>private</code>
等非 <code>public</code>
修饰的方法上，事务将失效。</p>
</li>
<li>
<p>如果在开启了事务的方法内，使用了 <code>try-catch</code>
语句块对异常进行了捕获，而没有将异常抛到外层，事务将不起效。</p>
</li>
<li>
<p>如果 A 方法开启了事务，B 方法没有开启事务，B 方法调用了 A 方法。如果 B 方法发生异常中，但不是调用的 A 方法产生的，则异常不会使 A 方法的事务回滚，此时事务无效。如果 B 方法发生异常中，异常是调用的 A 方法产生的，则 A 方法的事务回滚，此时事务有效。在 B 方法上加上注解 @Trasactional，这样 A 和 B 方法就在同一个事务里了，不管异常产生在哪里，事务都是有效的。</p>
<p>简单地说，当存在方法之间调用时，异常发生在无事务的方法中，但不是被调用的方法产生的，被调用的方法的事务无效。只有异常发生在开启事务的方法内，事务才有效。</p>
</li>
<li>
<p>如果使用了Spring + MVC，则 <code>context:component-scan</code>
重复扫描问题可能会引起事务失效。</p>
</li>
</ul>
<h4 id="原因分析">原因分析</h4>
<p>在应用系统调用声明 @Transactional 的目标方法时，Spring Framework 默认使用 AOP 代理，在代码运行时生成一个代理对象，再由这个代理对象来统一管理。Spring 事务是使用 AOP 环绕通知和异常通知，就是对方法进行拦截，在方法执行前开启事务，在捕获到异常时进行事务回滚，在方法执行完成后提交事务。</p>
<h3 id="最后">最后</h3>
<p>Spring 团队建议在具体的类（或类的方法）上使用 @Transactional 注解，而不要使用在类所要实现的任何接口上。在接口上使用 @Transactional 注解，只能当你设置了基于接口的代理时它才生效。因为注解是不能继承的，这就意味着如果正在使用基于类的代理时，那么事务的设置将不能被基于类的代理所识别，而且对象也将不会被事务代理所包装。</p>
<p>Spring 文档中写到：Spring AOP 部分使用 JDK 动态代理或者 CGLIB 来为目标对象创建代理，如果被代理的目标对象实现了至少一个接口，则会使用 JDK 动态代理。所有该目标类型实现的接口都将被代理。若该目标对象没有实现任何接口，则创建一个CGLIB代理。</p>
<h3 id="mysql-对语句的长度有限制默认是-4m"><code>Mysql</code> 对语句的长度有限制，默认是 4M。</h3>

        <br />

        <div>
            
            <a href="https://alittlesmile.gitee.io/blog/post/linux/centos%E4%BF%AE%E6%94%B9ali-yum%E6%BA%90/">&lt; CentOS 安装ali yum 源</a>
            

            
            <a href="https://alittlesmile.gitee.io/blog/post/diary/2022-03-26/" style="float:right">2022 03 26 ></a>
            
        </div>
    </article>

</div></body>
</html>
